//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include <Bvd1.h>
#include <bulverde.h>
#include <vkey.h>
#include <keypad.h>
#include "pcf50606.h"

static unsigned int local_mask;
static int is_initialized = 0;
static int iic_timeout = 0x100000;
static char devid = PCF50606_IIC_AD;
static int is_Onkey1sOff = 1;
int g_pcfBootStatus = 0;
PCF_IRQ  g_pcfIRQs_INT1[8];//Int1
PCF_IRQ  g_pcfIRQs_INT2;//Int2
PCF_IRQ  g_pcfIRQs_INT3[4];//Int3

void CDECL PcfIsr(irq_t irq, void *pvDevice, InterruptContext *pContext);
void PressEarpButton(void *pvDevice, bool *bArg);
void ReleaseEarpButton(void *pvDevice, bool *bArg);
void dumpRTCtime();

#define OPM_OFF   (0x00 << 5)
#define OPM_ON    (0x03 << 5)
#define OPM_ECO   (0x02 << 5)

EXTERN_C void PCF_SetDCDOpMOd(char opmode)
{
    writePCFregister(PCF50606_DCDC1, opmode & 0xC0);
    writePCFregister(PCF50606_DCDC2, opmode & 0xC0);
}

EXTERN_C void PCF_SetDCDEOpMOd(char opmode)
{
    writePCFregister(PCF50606_DCDEC1, opmode & 0xC0);
}

EXTERN_C void PCF_SetDCDUDOpMOd(char opmode)
{
    writePCFregister(PCF50606_DCUDC1, opmode & 0xC0);
}

EXTERN_C void PCF_SetIOREGOpMOd(char opmode)
{
    writePCFregister(PCF50606_IOREGC, opmode & 0xC0);
}

EXTERN_C void PCF_SetDxREGOpMOd(char opmode)
{
    writePCFregister(PCF50606_D1REGC1, opmode & 0xC0);
    writePCFregister(PCF50606_D2REGC1, opmode & 0xC0);
    writePCFregister(PCF50606_D3REGC1, opmode & 0xC0);
}

EXTERN_C void PCF_SetLPREGOpMOd(char opmode)
{
    writePCFregister(PCF50606_LPREGC1, opmode & 0xC0);
}

EXTERN_C void PCF_MoudleOpMod(char opmode)
{
    PCF_SetDCDOpMOd(opmode);
    PCF_SetDCDEOpMOd(opmode);
    PCF_SetDCDUDOpMOd(opmode);
    PCF_SetIOREGOpMOd(opmode);
    PCF_SetDxREGOpMOd(opmode);
}

EXTERN_C void PCFWatchDogEnable(Boolean IsEnable)
{
    char regData = readPCFregister(PCF50606_OOCC1);
    if (TRUE == IsEnable) {
        regData |= 0x08;
    }
    else {
        regData &= (~0x08);
    }
    writePCFregister(PCF50606_OOCC1, regData);
}

EXTERN_C void PCFWatchDogReset()
{
    char regData = readPCFregister(PCF50606_OOCC1);
    regData |= 0x02;
    writePCFregister(PCF50606_OOCC1, regData);
}

EXTERN_C void PCFOnkey1sOffEnable(Boolean IsEnable)
{
    if (TRUE == IsEnable) {
        is_Onkey1sOff = 1;
    }
    else {
        is_Onkey1sOff = 0;
        PCFWatchDogEnable(FALSE);
        PCFWatchDogReset();
    }
}

static void PCF50606_unmask_irq( unsigned int irq )
{
    unsigned int bit_value = ( irq - PCF50606_IRQ(0)) ;
    unsigned int mask = 1 << (bit_value % 8 );

    if ( bit_value > 15 ) {
        writePCFregister( PCF50606_INT3M, readPCFregister( PCF50606_INT3M ) & ~mask ) ;
    } else if ( bit_value > 7 ) {
        writePCFregister( PCF50606_INT2M, readPCFregister( PCF50606_INT2M ) & ~mask ) ;
    } else {
        writePCFregister( PCF50606_INT1M, readPCFregister( PCF50606_INT1M ) & ~mask ) ;
    }

    local_mask &= ~( 1 << bit_value );

}

void pcf50606_IrqDisable(void)
{
    ICMR &= ~(1 << 8);
}

void pcf50606_IrqEnable (void)
{
    GPDR0 &= ~(1 << 0);
    GAFR0_L &= ~((1 << 0) | (1 << 1)); //gpio0 is general gpio
    GFER0 |= (1 << 0);
    //GRER0 |= (1 << 0);
//    ICMR |= (1 << 8);
    DzRegisterIsr(GPIO_PCF50606_INT, 2, (isr_t)PcfIsr, NULL);
}

EXTERN_C Boolean Drv_PcfIrqInstall(uint_t PcfIrq, void *Arg, void * Handler)
{
    if ( PcfIrq < 0 || PcfIrq > MAX_PCF_INT ) {
        return FALSE;
    }
    PCF50606_unmask_irq(PcfIrq);//now unmask the relative interrupt
    if (PcfIrq < PCF50606_CHGINSM_IRQ) {
        g_pcfIRQs_INT1[PcfIrq].Handler = Handler;
        g_pcfIRQs_INT1[PcfIrq].Arg = Arg;
    }
    else if (PcfIrq < PCF50606_ADCRDYM_IRQ) {
        g_pcfIRQs_INT2.Handler = Handler;
        g_pcfIRQs_INT2.Arg = Arg;
    }
    else {
        g_pcfIRQs_INT3[PcfIrq - PCF50606_ADCRDYM_IRQ].Handler = Handler;
        g_pcfIRQs_INT3[PcfIrq - PCF50606_ADCRDYM_IRQ].Arg = Arg;
    }
    return TRUE;
}

EXTERN_C Boolean Drv_PcfAllChargeIrqInstall(void *Arg, void * Handler)
{
    PCF50606_unmask_irq(PCF50606_CHGINSM_IRQ);
    PCF50606_unmask_irq(PCF50606_CHGRMM_IRQ);
    PCF50606_unmask_irq(PCF50606_FCHGOKM_IRQ);
    PCF50606_unmask_irq(PCF50606_CHGERRM_IRQ);
    PCF50606_unmask_irq(PCF50606_CHGFRDYM_IRQ);
    PCF50606_unmask_irq(PCF50606_CHGPROTM_IRQ);
    PCF50606_unmask_irq(PCF50606_CHGWD10SM_IRQ);
    PCF50606_unmask_irq(PCF50606_CHGWDEXPM_IRQ);
    PCF50606_unmask_irq(PCF50606_LOWBATM_IRQ);
    PCF50606_unmask_irq(PCF50606_HIGHTMPM_IRQ);
    g_pcfIRQs_INT2.Handler = Handler;
    g_pcfIRQs_INT2.Arg = Arg;
    return TRUE;
}

void CDECL PcfIsr(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    long i;
    Boolean bDispatched = FALSE;
    GEDR0 |= (1 << 0);//clear GPIO_0 interrupt status bit

    char intStat[3], staMsk, chgMsk, modMsk;
//    cprintf("**********************************************\n");
    intStat[0] = readPCFregister( PCF50606_INT1 );
    intStat[1] = readPCFregister( PCF50606_INT2 );
    intStat[2] = readPCFregister( PCF50606_INT3 );

    staMsk = readPCFregister( PCF50606_INT1M );
    chgMsk = readPCFregister( PCF50606_INT2M );
    modMsk = readPCFregister( PCF50606_INT3M );
//    cprintf("INT1 %02x INT2 %02x INT3 %02x\n", intStat[0], intStat[1], intStat[2]);
    if ((~staMsk) & 0xFF) {
        for (i = 0; i < 8; i++) {
            if ( (intStat[0]) & ( 1 << i ) & (~staMsk) ) {
//              cprintf("staInt Bit%d\n", i);
                if(g_pcfIRQs_INT1[i].Handler) {
                    ((IsrHandler)g_pcfIRQs_INT1[i].Handler)(g_pcfIRQs_INT1[i].Arg, NULL);
                }
                bDispatched = TRUE;
            }
        }
    }

    if ((~chgMsk) & 0xFF || (~modMsk) & 0xF0) {
        if (intStat[1] || intStat[2] ) {
//            cprintf("Charge Interrupt %02x Battery Interrupt %02x\n", intStat[1], intStat[2]);
            if (g_pcfIRQs_INT2.Arg) {
                ((IsrHandler)g_pcfIRQs_INT2.Handler)((intStat+1), NULL);
                ((DzEvent*)(g_pcfIRQs_INT2.Arg))->NotifyByIsr();
            }
        }
    }

    if ((~modMsk) & 0x0F) {
        for (i = 0; i < 4; i++) {
            if ( (intStat[2]) & ( 1 << i ) & (~modMsk) ) {
//              cprintf("modInt Bit%d\n", i);
                if(g_pcfIRQs_INT3[i].Handler) {
                    ((IsrHandler)g_pcfIRQs_INT3[i].Handler)(g_pcfIRQs_INT3[i].Arg, NULL);
                }
                bDispatched = TRUE;
            }
        }
    }


    if (!bDispatched) {
        //bugbug:while an interrupt occur,the RTC periodic second interrupt occur.
#if defined(_DebugPCF)
        cprintf("unsupported!int state is :%x,mask is :%x\n", stat0, local_mask);
#endif
    }
//    cprintf("**********************************************\n");
}

void PcfDispatcherInit()
{
    int i;
    for (i = 0; i < 8; i++) {
        g_pcfIRQs_INT1[i].Handler = NULL;
        g_pcfIRQs_INT1[i].Arg = NULL;
    }

    for (i = 0; i < 4; i++) {
        g_pcfIRQs_INT3[i].Handler = NULL;
        g_pcfIRQs_INT3[i].Arg = NULL;
    }
    g_pcfIRQs_INT2.Handler = NULL;
    g_pcfIRQs_INT2.Arg = NULL;
    return;
}

static void emu_iic_delay(int us)
{
    int i;
    int va = 0;

    for(i = 0; i < us * 1400; i++)
    {
        va++;
    }
    return;
}

void writePCFregister( char address, char data )    // busy waiting
{
    int i;
    unsigned long flags;

    local_irq_save(flags);

    PWIDBR = devid & 0xFE;
    PWICR |= ICR_START;
    PWICR &= ~(ICR_STOP);
    PWICR &= ~(ICR_ALDIE);
    PWICR |= ICR_TB;

    i = 0;
    while (!(PWISR & ISR_ITE) && (i++ < iic_timeout));
    PWISR |= ISR_ITE;

    PWIDBR = address;
    PWICR &= ~(ICR_START);
    PWICR &= ~(ICR_STOP);
    PWICR &= ~(ICR_ALDIE);
    PWICR |= ICR_TB;

    i = 0;
    while (!(PWISR & ISR_ITE) && (i++ < iic_timeout));
    PWISR |= ISR_ITE;

    PWIDBR = data;
    PWICR &= ~(ICR_START);
    PWICR |= ICR_STOP;
    PWICR &= ~(ICR_ALDIE);
    PWICR |= ICR_TB;

    i = 0;
    while (!(PWISR & ISR_ITE) && (i++ < iic_timeout));
    PWISR |= ISR_ITE;

    PWICR &= ~(ICR_STOP);
    local_irq_restore( flags );

    return;
}


char readPCFregister( char address )    // busy waiting
{
    int i;
    unsigned long flags;

    local_irq_save(flags);

    PWIDBR = devid & 0xFE;
    PWICR |= ICR_START;
    PWICR &= ~(ICR_STOP);
    PWICR &= ~(ICR_ALDIE);
    PWICR |= ICR_TB;

    i = 0;
    while (!(PWISR & ISR_ITE) && (i++ < iic_timeout));
    PWISR |= ISR_ITE;

    PWIDBR = address;
    PWICR &= ~(ICR_START);
    PWICR &= ~(ICR_STOP);
    PWICR &= ~(ICR_ALDIE);
    PWICR |= ICR_TB;

    i = 0;
    while (!(PWISR & ISR_ITE) && (i++ < iic_timeout));
    PWISR |= ISR_ITE;

    PWIDBR = devid | 0x01;
    PWICR |= ICR_START;
    PWICR &= ~(ICR_STOP);
    PWICR &= ~(ICR_ALDIE);
    PWICR |= ICR_TB;

    i = 0;
    while (!(PWISR & ISR_ITE) && (i++ < iic_timeout));
    PWISR |= ISR_ITE;

    PWICR &= ~(ICR_START);
    PWICR |= ICR_STOP;
    PWICR &= ~(ICR_ALDIE);
    PWICR |= ICR_ACKNAK;
    PWICR |= ICR_TB;

    i = 0;
    while (!(PWISR & ISR_IRF) && (i++ < iic_timeout));
    PWISR |= ISR_IRF;

    PWICR &= ~(ICR_STOP);
    PWICR &= ~(ICR_ACKNAK);

    local_irq_restore( flags );
    return PWIDBR;
}

void i2c_pcf5060x_workaround()
{
    int i;

    GAFR0_L &= ~(0x3c0);
    GPSR0 = (1 << 3);
    GPDR0 &= ~(1 << 4);     //SDA
    GPDR0 |= (1 << 3);      //SCL

    for(i = 0; i < 10; i++)
    {
        emu_iic_delay(1);
        GPCR0 = (1 << 3);
        emu_iic_delay(1);
        GPSR0 = (1 << 3);
    }

    return;
}
unsigned long pcf50606_get_boot_status();
void  pcf50606_init_irq(void)
{
    g_pcfBootStatus = readPCFregister( PCF50606_INT3 ) << 16 |
             readPCFregister( PCF50606_INT2 ) << 8  |
             readPCFregister( PCF50606_INT1 );//clear all interrupt and save boot status

//    cprintf("booting status %x\n",g_pcfBootStatus);

    if ( pcf50606_get_boot_status()==2 ) {
        cprintf("boot while charging\n");
    }
    else if ( pcf50606_get_boot_status()==1 ) {
        cprintf("boot from alrm\n");
    }
    else if( pcf50606_get_boot_status()==0 ) {
        cprintf("boot normal\n");
    }

    writePCFregister( PCF50606_INT1M , 0xDF );
    writePCFregister( PCF50606_INT2M , 0xFF );
    writePCFregister( PCF50606_INT3M , 0xFF );

    local_mask = readPCFregister( PCF50606_INT3M ) << 16 |
                 readPCFregister( PCF50606_INT2M ) << 8 |
                 readPCFregister( PCF50606_INT1M ) ;

}

void setOOCC2()
{
    writePCFregister (PCF50606_OOCC2,0x0f);
}

void resetOOCC2()
{
    writePCFregister (PCF50606_OOCC2,0x01);
}

void pcf_prepare_poweroff()
{
    writePCFregister(PCF50606_GPOC1, 0x07);//force led off
    writePCFregister(PCF50606_GPOC2, 0x00);//force keypad backlight off
    writePCFregister(PCF50606_GPOC3, 0x00);//force led off
}

void pcf50606_power_off(void)
{
    writePCFregister( PCF50606_MBCC3, 0x3a );
    //Switch RED/BLUE led off
    pcf_prepare_poweroff();
    //lcd backlight off
    GAFR0_U &= ~(0x3);
    GPCR0 = 0x10000;
    DzDelay(5000);

    dumpRTCtime();

    {
        cli();
        cprintf("\n\nPOWER OFF NOW\n\n");
        writePCFregister(PCF50606_LPREGC2,0);
        DzDelay(3000);
        writePCFregister(PCF50606_OOCC1,readPCFregister(PCF50606_OOCC1)|0x1);
        //should not excute to here.
        while(1) {
            DzDelay(3000*1000);
            cprintf("\n\nPOWER OFF NOW\n\n");
        };
    }
}

void PressOnKey1S(void *pvDevice, Boolean *bArg)
{
    KPD_DATA inputdata;
    inputdata.keycode = VK_Power;
    inputdata.status = KEYDOWN;
    InputNotifyByIsr(EVENT_KEYPAD, 0x12, &inputdata);
    cputs("P");
    if (0 == is_Onkey1sOff) {
        PCFWatchDogEnable(FALSE);
        PCFWatchDogReset();
    }
    else {
        cputs("O");
        pcf_prepare_poweroff();
    }
}

void HoldOnKey(void *pvDevice, bool *bArg)
{
    cputs("K");
    KPD_DATA inputdata;
    inputdata.keycode = VK_PHONE_HANG_UP;
    inputdata.status = KEYDOWN;
    InputNotifyByIsr(EVENT_KEYPAD, 0x12, &inputdata);
}

void ReleaseOnKey(void *pvDevice, bool *bArg)
{
    KPD_DATA inputdata;
    inputdata.keycode = VK_PHONE_HANG_UP;
    inputdata.status = KEYUP;
    InputNotifyByIsr(EVENT_KEYPAD, 0x12, &inputdata);
}

void EnableEarpBtnDetection(Boolean enable)
{
    if(enable) {
        writePCFregister(PCF50606_ACDC1,0x82);//enable acd detection

        char volt = readPCFregister(PCF50606_ADCC2);
        volt = 0x8a;
        writePCFregister(PCF50606_ADCC2,volt);  // adcin2
        DzDelay(100);
        volt = 0x8b;
        writePCFregister(PCF50606_ADCC2,volt);//start
    }
    else {
        writePCFregister(PCF50606_ACDC1,0x0);//disable acd detection
    }
    DzDelay(100);

    //cprintf("acdc1 %x,adcs1 %x,adcs2 %x\n",readPCFregister(PCF50606_ACDC1),readPCFregister(PCF50606_ADCS1),readPCFregister(PCF50606_ADCS2));
}

int pcf50606_getADCS()
{
    char volt = readPCFregister(PCF50606_ADCC2);
    volt = 0x8a;
    writePCFregister(PCF50606_ADCC2,volt);  // adcin2
    DzDelay(100);
    volt = 0x8b;
    writePCFregister(PCF50606_ADCC2,volt);//start
    return 4*readPCFregister(PCF50606_ADCS1)+ readPCFregister(PCF50606_ADCS2);
}

void pcf50606_dump_volt();
EXTERN_C void PCF50606_Reset_Manual();
void HeadSetWake(Boolean isFromSleep);
void pcf50606_dump_registers();
ECode pcf50606_init(void)
{
    char id;

    // initialize power i2c
    i2c_pcf5060x_workaround();
    DzDelay(1);
    if (is_initialized == 1) return NOERROR;

	PcfDispatcherInit();
//    pcf50606_IrqEnable();
//    pcf50606_IrqDisable();
    // enable clock
    CKEN |= CKEN15_PI2C;

    // enable PWR_SDA, PWR_SCL pins
    PCFR |= PCFR_PI2C_EN;

    // slave address to 0
    PWISAR = 0x0;

    // control register to 0
    PWICR |= (1 << 15);
    PWICR |= ICR_IUE;           // unit enable
    PWICR |= ICR_SCLE;          // SCL enable

    writePCFregister(PCF50606_OOCC1, 0x24);//Set no wakeup condition for EXTON
    //pcf50606_dump_registers();

    // read ID
    id = readPCFregister( PCF50606_ID );
    if ( id != 0x92 )           // init fail
        return E_FAIL;

    pcf50606_init_irq();

//    DzRegisterIsr(GPIO_PCF50606_INT, 2, (isr_t)PcfIsr, NULL);

    Drv_PcfIrqInstall(PCF50606_ONKEY1SM_IRQ,NULL,(void *)PressOnKey1S);
    Drv_PcfIrqInstall(PCF50606_ONKEYFM_IRQ,NULL,(void *)HoldOnKey);
    Drv_PcfIrqInstall(PCF50606_ONKEYRM_IRQ,NULL,(void *)ReleaseOnKey);
    Drv_PcfIrqInstall(PCF50606_ACDINSM_IRQ,NULL,(void *)PressEarpButton);
    Drv_PcfIrqInstall(PCF50606_ACDREMM_IRQ,NULL,(void *)ReleaseEarpButton);
    is_initialized = 1;

    //pcf50606_dump_volt();//dump voltage
    //PCF50606_Reset_Manual();//config pcf
    resetOOCC2();

    HeadSetWake(0);//Enable earpbtn detect while headset plug

    return NOERROR;
}

void pcf50606_dump_dispatcher()
{
    for(int i=0;i<8;i++)
    {
        cprintf("dispatcher address %x\n",g_pcfIRQs_INT1[i].Handler);
    }
    cprintf("dispatcher address %x\n",g_pcfIRQs_INT2.Handler);
    for(int i=0;i<4;i++)
    {
        cprintf("dispatcher address %x\n",g_pcfIRQs_INT3[i].Handler);
    }
}

int pcf50606_sleep(void)
{
    PcfIsr(0,0,0);
    return 0;
}

ECode pcf50606_wake(void)
{
    char id;

    // initialize power i2c
    i2c_pcf5060x_workaround();
    DzDelay(1);

    // enable clock
    CKEN |= CKEN15_PI2C;

    // enable PWR_SDA, PWR_SCL pins
    PCFR |= PCFR_PI2C_EN;

    // slave address to 0
    PWISAR = 0x0;

    // control register to 0
    PWICR |= (1 << 15);
    PWICR |= ICR_IUE;           // unit enable
    PWICR |= ICR_SCLE;          // SCL enable

    // read ID
    id = readPCFregister( PCF50606_ID );
    if ( id != 0x92 )           // init fail
        return E_FAIL;

    // clear pcf interrupt
//    readPCFregister( PCF50606_INT3 ) << 16 |
//           readPCFregister( PCF50606_INT2 ) << 8  |
//           readPCFregister( PCF50606_INT1 );//clear all interrupt
    PcfIsr(0,0,0);
    // enable gpio0 interrupt
    GPDR0 &= ~(1 << 0);
    GAFR0_L &= ~((1 << 0) | (1 << 1)); //gpio0 is general gpio
    GFER0 |= (1 << 0);
    //GRER0 |= (1 << 0);

    return NOERROR;
}

/*
 return 0: Onkey boot
        1: alarm booot (default)
        2: charger insert boot
        3: reboot
 */
unsigned long pcf50606_get_boot_status()
{
    static int s_bootstat = -1;
    if (s_bootstat >= 0) return s_bootstat;

    if ( readPCFregister(PCF50606_OOCS) & 0x20 ) {
        s_bootstat = 2;//charger insert boot, detect
        return s_bootstat;
    }

    if ( g_pcfBootStatus & 0x1 ) {
        s_bootstat = 0;//Onkey boot
        return s_bootstat;
    }

    if(RCSR&0x2) {
        s_bootstat = 3;//reboot
        return s_bootstat;
    }

    s_bootstat = 1;
    return s_bootstat;//other:alarm boot
}

#if 0
static char * pcf_reg[] = {
    "PCF50606_ID",
    "PCF50606_OOCS",
    "PCF50606_INT1",
    "PCF50606_INT2",
    "PCF50606_INT3",
    "PCF50606_INT1M",
    "PCF50606_INT2M",
    "PCF50606_INT3M",
    "PCF50606_OOCC1",
    "PCF50606_OOCC2",
    "PCF50606_RTCSC",
    "PCF50606_RTCMN",
    "PCF50606_RTCHR",
    "PCF50606_RTCWD",
    "PCF50606_RTCDT",
    "PCF50606_RTCMT",
    "PCF50606_RTCYR",
    "PCF50606_RTCSCA",
    "PCF50606_RTCMNA",
    "PCF50606_RTCHRA",
    "PCF50606_RTCWDA",
    "PCF50606_RTCDTA",
    "PCF50606_RTCMTA",
    "PCF50606_RTCYRA",
    "PCF50606_PSSC",
    "PCF50606_PWROKM",
    "PCF50606_PWROKS",
    "PCF50606_DCDC1",
    "PCF50606_DCDC2",
    "PCF50606_DCDC3",
    "PCF50606_DCDC4",
    "PCF50606_DCDEC1",
    "PCF50606_DCDEC2",
    "PCF50606_DCUDC1",
    "PCF50606_DCUDC2",
    "PCF50606_IOREGC",
    "PCF50606_D1REGC1",
    "PCF50606_D2REGC1",
    "PCF50606_D3REGC1",
    "PCF50606_LPREGC1",
    "PCF50606_LPREGC2",
    "PCF50606_MBCC1",
    "PCF50606_MBCC2",
    "PCF50606_MBCC3",
    "PCF50606_MBCS1",
    "PCF50606_BBCC",
    "PCF50606_ADCC1",
    "PCF50606_ADCC2",
    "PCF50606_ADCS1",
    "PCF50606_ADCS2",
    "PCF50606_ADCS3",
    "PCF50606_ACDC1",
    "PCF50606_BVMC",
    "PCF50606_PWMC1",
    "PCF50606_LEDC1",
    "PCF50606_LEDC2",
    "PCF50606_GPOC1",
    "PCF50606_GPOC2",
    "PCF50606_GPOC3",
    "PCF50606_GPOC4",
    "PCF50606_GPOC5"
};
#endif

void pcf50606_dump_registers()
{
    int i;
    for(i=PCF50606_ID;i<PCF50606_GPOC5;i++)
        kprintf("reg %02x    %02x\n",i,readPCFregister(i));
}

typedef struct parvol {
    char    par;
    char    vol;
} parvol_t;

static parvol_t D123parvol[] =
{
    { 0x00,  9 }, { 0x01, 10 }, { 0x02, 11 }, { 0x03, 12 },
    { 0x04, 13 }, { 0x05, 14 }, { 0x06, 15 }, { 0x07, 16 },
    { 0x08, 17 }, { 0x09, 18 }, { 0x0a, 19 }, { 0x0b, 20 },
    { 0x0c, 21 }, { 0x0d, 22 }, { 0x0e, 23 }, { 0x0f, 24 },
    { 0x10, 25 }, { 0x11, 26 }, { 0x12, 27 }, { 0x13, 28 },
    { 0x14, 29 }, { 0x15, 30 }, { 0x16, 31 }, { 0x17, 32 },
    { 0x18, 33 }, { 0xFF, 00 }
};


int MyStrCmp(const char *s1, const char *s2, int maxlen)
{
    int i;

    for(i = 0; i < maxlen; i++) {
        if(s1[i] != s2[i])
            return 0;
        if(s1[i] == 0)
            return 1;
    }

    return 1;
}

static char readD1REGvalue(void)
{
    char value, param;
    int i;

    value = readPCFregister( PCF50606_D1REGC1 );
    if (( value & 0x00E0 ) == 0x0 )
        return 0;

    param = value & 0x001F;

    for ( i = 0 ; D123parvol[i].par != 0xFF ; i++ ) {
        if ( param == D123parvol[i].par ) {
            return D123parvol[i].vol;
        }
    }
    return 0;
}

static void writeD1REGvalue(char value)
{
    int i;

    for ( i = 0 ; D123parvol[i].par != 0xFF ; i++ ) {
        if ( value == D123parvol[i].vol ) {
            writePCFregister( PCF50606_D1REGC1 , D123parvol[i].par | PMIC_OPMOD7 );
            return;
        }
    }
    // write 0 value , disable power controller
    writePCFregister( PCF50606_D1REGC1 , PMIC_OPMOD0 );
    return;
}

static char readD2REGvalue(void)
{
    char value, param;
    int i;

    value = readPCFregister( PCF50606_D2REGC1 );
    if (( value & 0x00E0 ) == 0x0 )
        return 0;

    param = value & 0x001F;

    for ( i = 0 ; D123parvol[i].par != 0xFF ; i++ ) {
        if ( param == D123parvol[i].par ) {
            return D123parvol[i].vol;
        }
    }
    return 0;
}

static char readD3REGvalue(void)
{
    char value, param;
    int i;

    value = readPCFregister( PCF50606_D3REGC1 );
    if (( value & 0x00E0 ) == 0x0 )
        return 0;

    param = value & 0x001F;

    for ( i = 0 ; D123parvol[i].par != 0xFF ; i++ ) {
        if ( param == D123parvol[i].par ) {
            return D123parvol[i].vol;
        }
    }
    return 0;
}

static void writeD3REGvalue(char value)
{
    int i;

    for ( i = 0 ; D123parvol[i].par != 0xFF ; i++ ) {
        if ( value == D123parvol[i].vol ) {
            writePCFregister( PCF50606_D3REGC1 , D123parvol[i].par | PMIC_OPMOD7 );
            return;
        }
    }
    // write 0 value , disable power controller
    writePCFregister( PCF50606_D3REGC1 , PMIC_OPMOD0 );
    return;
}

static parvol_t IOparvol[] =
{
    { 0x09, 18 }, { 0x0a, 19 }, { 0x0b, 20 }, { 0x0c, 21 },
    { 0x0d, 22 }, { 0x0e, 23 }, { 0x0f, 24 }, { 0x10, 25 },
    { 0x11, 26 }, { 0x12, 27 }, { 0x13, 28 }, { 0x14, 29 },
    { 0x15, 30 }, { 0x16, 31 }, { 0x17, 32 }, { 0x18, 33 },
    { 0xFF, 00 }
};


static char readIOREGvalue(void)
{
    char value, param;
    int i;

    value = readPCFregister( PCF50606_IOREGC );
    if (( value & 0x00E0 ) == 0x0 )
        return 0;

    param = value & 0x001F;

    for ( i = 0 ; IOparvol[i].par != 0xFF ; i++ ) {
        if ( param == IOparvol[i].par ) {
            return IOparvol[i].vol;
        }
    }
    return 0;
}

static char readLPREGvalue(void)
{
    char value, param;
    int i;

    value = readPCFregister( PCF50606_LPREGC1 );
    if (( value & 0x00E0 ) == 0x0 )
        return 0;

    param = value & 0x001F;

    for ( i = 0 ; D123parvol[i].par != 0xFF ; i++ ) {
        if ( param == D123parvol[i].par ) {
            return D123parvol[i].vol;
        }
    }
    return 0;
}


static parvol_t DCDEparvol[] =
{
    { 0x00,  9 }, { 0x01, 12 }, { 0x02, 15 }, { 0x03, 18 },
    { 0x04, 21 }, { 0x05, 24 }, { 0x06, 27 }, { 0x07, 30 },
    { 0x08, 33 }, { 0x09, 33 }, { 0x0a, 33 }, { 0x0b, 33 },
    { 0x0c, 33 }, { 0x0d, 33 }, { 0x0e, 33 }, { 0x0f, 33 },
    { 0xFF, 00 }
};

static char readDCDEvalue(void)
{
    char value, param;
    int i;

    value = readPCFregister( PCF50606_DCDEC1 );
    if (( value & 0x00E0 ) == 0x0 )
        return 0;

    param = value & 0x000F;

    for ( i = 0 ; DCDEparvol[i].par != 0xFF ; i++ ) {
        if ( param == DCDEparvol[i].par ) {
            return DCDEparvol[i].vol;
        }
    }
    return 0;
}


static parvol_t DCDparvol[] =
{
    { 0x00,  9 }, { 0x01,  9 }, { 0x02,  9 }, { 0x03,  9 },
    { 0x04, 10 }, { 0x05, 10 }, { 0x06, 10 }, { 0x07, 10 },
    { 0x08, 11 }, { 0x09, 11 }, { 0x0a, 11 }, { 0x0b, 11 },
    { 0x0c, 12 }, { 0x0d, 12 }, { 0x0e, 12 }, { 0x0f, 12 },
    { 0x10, 13 }, { 0x11, 13 }, { 0x12, 13 }, { 0x13, 13 },
    { 0x14, 14 }, { 0x15, 14 }, { 0x16, 14 }, { 0x17, 14 },
    { 0x18, 15 }, { 0x19, 18 }, { 0x1A, 21 }, { 0x1B, 24 },
    { 0x1C, 27 }, { 0x1D, 30 }, { 0x1E, 33 }, { 0x1F, 36 },
    { 0xFF, 00 }
};

static char readDCDvalue(void)
{ // TODO
    char value, param;
    int i;

    value = readPCFregister( PCF50606_DCDC4 );

    if ( value & 0x0080 ) { // DCDC2
        value = readPCFregister( PCF50606_DCDC2 );
    } else {        // DCDC1
        value = readPCFregister( PCF50606_DCDC1 );
    }

    if (( value & 0x00E0 ) == 0x0 )
        return 0;

    param = value & 0x001F;

    for ( i = 0 ; DCDparvol[i].par != 0xFF ; i++ ) {
        if ( param == DCDparvol[i].par ) {
            return DCDparvol[i].vol ;
        }
    }

    return 0;
}

static void writeDCDvalue( char value )
{
    char temp;
    int i;

    for ( i = 0 ; DCDparvol[i].par != 0xFF ; i++ ) {
        if ( value == DCDparvol[i].vol ) {
            temp = readPCFregister( PCF50606_DCDC4 );
            if ( value & 0x0080 ) { // DCDC2
                writePCFregister( PCF50606_DCDC2, DCDparvol[i].par | PMIC_OPMOD7 );
            } else {
                writePCFregister( PCF50606_DCDC1, DCDparvol[i].par | PMIC_OPMOD7 );
            }
            return;
        }
    }
    return;
}

static parvol_t DCUDparvol[] =
{
    { 0x00,  9 }, { 0x01, 12 }, { 0x02, 15 }, { 0x03, 18 },
    { 0x04, 21 }, { 0x05, 24 }, { 0x06, 27 }, { 0x07, 30 },
    { 0x08, 33 }, { 0x09, 33 }, { 0x0a, 33 }, { 0x0b, 33 },
    { 0x0c, 33 }, { 0x0d, 33 }, { 0x0e, 33 }, { 0x0f, 33 },
    { 0x10, 40 }, { 0x11, 41 }, { 0x12, 42 }, { 0x13, 43 },
    { 0x14, 44 }, { 0x15, 45 }, { 0x16, 46 }, { 0x17, 47 },
    { 0x18, 48 }, { 0x19, 49 }, { 0x1A, 50 }, { 0x1B, 51 },
    { 0x1C, 52 }, { 0x1D, 53 }, { 0x1E, 54 }, { 0x1F, 55 },
    { 0xFF, 00 }
};

static char readDCUDvalue(void)
{
    char value, param;
    int i;

    value = readPCFregister( PCF50606_DCUDC1 );
    if (( value & 0x00E0 ) == 0x0 )
        return 0;

    param = value & 0x001F;

    for ( i = 0 ; DCUDparvol[i].par != 0xFF ; i++ ) {
        if ( param == DCUDparvol[i].par ) {
            return DCUDparvol[i].vol;
        }
    }
    return 0;
}

static void writeDCUDvalue(char value)
{
    int i;

    for ( i = 0 ; DCUDparvol[i].par != 0xFF ; i++ ) {
        if ( value == DCUDparvol[i].vol ) {
            writePCFregister( PCF50606_DCUDC1 , DCUDparvol[i].par | PMIC_OPMOD7 );
            return;
        }
    }
    // write 0 value , disable power controller
    writePCFregister( PCF50606_DCUDC1 , PMIC_OPMOD0 );
    return;
}

void pcf50606_pmu_set_voltage( const char *regname, int value )
{
    if ( pcf50606_init() != 0 ) {
        return;
    }
    if ( MyStrCmp( (const char *)regname, (const char *)"D1REG", 5 ) ) {
        writeD1REGvalue( (char) value );
    }
    else if ( MyStrCmp( (const char *)regname, (const char *)"D2REG", 5 ) ) {
    }
    else if ( MyStrCmp( (const char *)regname, (const char *)"D3REG", 5 ) ) {
        writeD3REGvalue( (char) value );
    }
    else if ( MyStrCmp( (const char *)regname, (const char *)"IOREG", 5 ) ) {
    }
    else if ( MyStrCmp( (const char *)regname, (const char *)"LPREG", 5 ) ) {
    }
    else if ( MyStrCmp( (const char *)regname, (const char *)"DCUD", 4 ) ) {
        writeDCUDvalue( (char) value );
    }
    else if ( MyStrCmp( (const char *)regname, (const char *)"DCDE", 4 ) ) {
    }
    else if ( MyStrCmp( (const char *)regname, (const char *)"DCD", 3 ) ) {
        writeDCDvalue( (char) value );
    } else {
    }
}

void pcf50606_dump_volt()
{
    cprintf("power source voltage dump infomation\n");
    cprintf("D1REG %d,for VCC_PLL\n",readD1REGvalue());
    cprintf("D2REG %d,for VCC_SRAM\n",readD2REGvalue());
    cprintf("D3REG %d,for Camera & WM9713\n",readD3REGvalue());
    cprintf("DCDE %d,for VCC_MEM,PMIC,DOC,Camera\n",readDCDEvalue());
    cprintf("DCD %d,for VCC_CORE\n",readDCDvalue());
    cprintf("DCUD %d,for Audio PA\n",readDCUDvalue());
    cprintf("IOREG %d,for VCC_IO,VCC_USIM,VCC_LCD,LCM,Camera\n",readIOREGvalue());
    cprintf("LPREG %d,for VCC_BATT\n",readLPREGvalue());
}


#define REG_ST  0x00
#define REG_OT  0x01
#define REG_SOT 0x03
#define REG_SCT 0x04
#define REG_XXT 0x05
#define REG_RCT 0x06

char *RegisterIDString[] = {
        "PCF50606_ID",
        "PCF50606_OOCS",
        "PCF50606_INT1",
        "PCF50606_INT2",
        "PCF50606_INT3",
        "PCF50606_INT1M",
        "PCF50606_INT2M",
        "PCF50606_INT3M",
        "PCF50606_OOCC1",
        "PCF50606_OOCC2",
        "PCF50606_RTCSC",
        "PCF50606_RTCMN",
        "PCF50606_RTCHR",
        "PCF50606_RTCWD",
        "PCF50606_RTCDT",
        "PCF50606_RTCMT",
        "PCF50606_RTCYR",
        "PCF50606_RTCSCA",
        "PCF50606_RTCMNA",
        "PCF50606_RTCHRA",
        "PCF50606_RTCWDA",
        "PCF50606_RTCDTA",
        "PCF50606_RTCMTA",
        "PCF50606_RTCYRA",
        "PCF50606_PSSC",
        "PCF50606_PWROKM",
        "PCF50606_PWROKS",
        "PCF50606_DCDC1 ",
        "PCF50606_DCDC2 ",
        "PCF50606_DCDC3 ",
        "PCF50606_DCDC4 ",
        "PCF50606_DCDEC1",
        "PCF50606_DCDEC2",
        "PCF50606_DCUDC1",
        "PCF50606_DCUDC2",
        "PCF50606_IOREGC",
        "PCF50606_D1REGC1",
        "PCF50606_D2REGC1",
        "PCF50606_D3REGC1",
        "PCF50606_LPREGC1",
        "PCF50606_LPREGC2",
        "PCF50606_MBCC1",
        "PCF50606_MBCC2",
        "PCF50606_MBCC3",
        "PCF50606_MBCS1",
        "PCF50606_BBCC",
        "PCF50606_ADCC1",
        "PCF50606_ADCC2",
        "PCF50606_ADCS1",
        "PCF50606_ADCS2",
        "PCF50606_ADCS3",
        "PCF50606_ACDC1",
        "PCF50606_BVMC",
        "PCF50606_PWMC1",
        "PCF50606_LEDC1",
        "PCF50606_LEDC2",
        "PCF50606_GPOC1",
        "PCF50606_GPOC2",
        "PCF50606_GPOC3",
        "PCF50606_GPOC4",
        "PCF50606_GPOC5",
        "",
    };

char ResetValue[][3] = {
/*0x00*/        {PCF50606_ID,      REG_ST,  0x00},//@
/*0x01*/        {PCF50606_OOCS,    REG_XXT, 0x00},//@RSTN is not asserted in supply voltage failure situations
/*0x02*/        {PCF50606_INT1,    REG_RCT, 0x00},//@Read to clear
/*0x03*/        {PCF50606_INT2,    REG_RCT, 0x00},//@Read to clear
/*0x04*/        {PCF50606_INT3,    REG_RCT, 0x00},//@Read to clear
/*0x05*/        {PCF50606_INT1M,   REG_RCT/*REG_ST*/,  0xFF},//Mask all interrupt, Reserved bit write 0
/*0x06*/        {PCF50606_INT2M,   REG_RCT/*REG_ST*/,  0xFF},//Mask all interrupt
/*0x07*/        {PCF50606_INT3M,   REG_RCT/*REG_ST*/,  0xFF},//Mask all interrupt, Reserved bit write 0
/*0x08*/        {PCF50606_OOCC1,   REG_ST,  0x64},//CLK32 activated, RTC alarm/Charger insertion wake-up
/*0x09*/        {PCF50606_OOCC2,   REG_ST,  0x05},//ONKEYDB 14 ms, EXTONDB 14 ms
/*0x0a*/        {PCF50606_RTCSC,   REG_RCT, 0x00},//For Read
/*0x0b*/        {PCF50606_RTCMN,   REG_RCT, 0x00},//For Read
/*0x0c*/        {PCF50606_RTCHR,   REG_RCT, 0x00},//For Read
/*0x0d*/        {PCF50606_RTCWD,   REG_RCT, 0x00},//For Read
/*0x0e*/        {PCF50606_RTCDT,   REG_RCT, 0x00},//For Read
/*0x0f*/        {PCF50606_RTCMT,   REG_RCT, 0x00},//For Read
/*0x10*/        {PCF50606_RTCYR,   REG_RCT, 0x00},//For Read
/*0x11*/        {PCF50606_RTCSCA,  REG_RCT, 0x00},//For Read
/*0x12*/        {PCF50606_RTCMNA,  REG_RCT, 0x00},//For Read
/*0x13*/        {PCF50606_RTCHRA,  REG_RCT, 0x00},//For Read
/*0x14*/        {PCF50606_RTCWDA,  REG_RCT, 0x00},//For Read
/*0x15*/        {PCF50606_RTCDTA,  REG_RCT, 0x00},//For Read
/*0x16*/        {PCF50606_RTCMTA,  REG_RCT, 0x00},//For Read
/*0x17*/        {PCF50606_RTCYRA,  REG_RCT, 0x00},//For Read
/*0x18*/        {PCF50606_PSSC,    REG_ST,  0x39},//IO regulator/DCD converter/D1 regulator/D2 regulator activates in phase 2
/*0x19*/        {PCF50606_PWROKM,  REG_ST,  0xFF},//Mask ALL
/*0x1a*/        {PCF50606_PWROKS,  REG_RCT, 0xFF},//Read Only
/*0x1b*/        {PCF50606_DCDC1,   REG_OT,  0xd0},//1.300v, 0b110 opmode
/*0x1c*/        {PCF50606_DCDC2,   REG_OT,  0xc0},//0.900v, 0b110 opmode
/*0x1d*/        {PCF50606_DCDC3,   REG_ST,  0x00},//DVMMOD 0b000 DVMTIM = 0: instantaneous switching
/*0x1e*/        {PCF50606_DCDC4,   REG_ST,  0x30},//Current limit setting 1.25a, Register DCDC2 defines the output voltage and operation mode
/*0x1f*/        {PCF50606_DCDEC1,  REG_OT,  0x83},//1.8v, 0b100 opmode
/*0x20*/        {PCF50606_DCDEC2,  REG_ST,  0x00},//Reset Value
/*0x21*/        {PCF50606_DCUDC1,  REG_OT,  0x9a},//0b11010 5.00v
/*0x22*/        {PCF50606_DCUDC2,  REG_ST,  0x30},//Current limit setting 1.25a
/*0x23*/        {PCF50606_IOREGC,  REG_OT,  0xf5},//0b10101 3.0v opmode 0b111
/*0x24*/        {PCF50606_D1REGC1, REG_OT,  0xc2},//0b00010 1.1v opmode 0b110
/*0x25*/        {PCF50606_D2REGC1, REG_OT,  0xc4},//0b00100 1.3v opmode 0b110
/*0x26*/        {PCF50606_D3REGC1, REG_OT,  0x90},//0b10000 1.0v opmode 0b100
/*0x27*/        {PCF50606_LPREGC1, REG_SOT, 0xf5},//0b10101 3.0v opmode 0b111
/*0x28*/        {PCF50606_LPREGC2, REG_ST,  0x01},//Mode in STANDBY and SAVE state: ECO, And No Reset LP regulator when system is in STANDBY
/*0x29*/        {PCF50606_MBCC1,   REG_SCT, 0x1f},//CHGAPE/AUTOFS Enbale, CHGMOD idle, Charger detection mode 0
/*0x2a*/        {PCF50606_MBCC2,   REG_ST,  0x14},//charging time 60min
/*0x2b*/        {PCF50606_MBCC3,   REG_ST,  0x3a},//Charge Control voltage:4.2v, current: 0.40 * IFAST
/*0x2c*/        {PCF50606_MBCS1,   REG_RCT, 0x00},//Read only
/*0x2d*/        {PCF50606_BBCC,    REG_ST,  0x11},//BB Charger ON, BB Charger voltage 3.0v
/*0x2e*/        {PCF50606_ADCC1,   REG_OT,  0x00},//Reset Value
/*0x2f*/        {PCF50606_ADCC2,   REG_OT,  0x00},//Rsset Value
/*0x30*/        {PCF50606_ADCS1,   REG_RCT, 0x00},//Read Only
/*0x31*/        {PCF50606_ADCS2,   REG_RCT, 0x00},//Read Only
/*0x32*/        {PCF50606_ADCS3,   REG_RCT, 0x00},//Read Only
/*0x33*/        {PCF50606_ACDC1,   REG_ST,  0x00},//Threshold voltage:1.00v, Disable ACD module, Enable 14ms debounce filter
/*0x34*/        {PCF50606_BVMC,    REG_ST,  0x0e},//Threshold voltage:3.40v, Enable 62ms debounce filter
/*0x35*/        {PCF50606_PWMC1,   REG_SOT, 0x80},//Defines PWM clock frequency:100: 3.6MHz / 64 = 56.3kHz, Defines mode during ACTIVE state:OFF
/*0x36*/        {PCF50606_LEDC1,   REG_ST,  0x4d},//Reset Value
/*0x37*/        {PCF50606_LEDC2,   REG_ST,  0x4d},//Reset Value
/*0x38*/        {PCF50606_GPOC1,   REG_ST,  0x04},//Reset Value
/*0x39*/        {PCF50606_GPOC2,   REG_ST,  0x05},//Reset Value
/*0x3a*/        {PCF50606_GPOC3,   REG_ST,  0x05},//Reset Value
/*0x3b*/        {PCF50606_GPOC4,   REG_ST,  0x00},//High Z
/*0x3c*/        {PCF50606_GPOC5,   REG_ST,  0x00},//High Z
    };


EXTERN_C void PCF50606_Reset_Manual()
{
    int idx = 0;
    char regData;
    for (idx = 0; idx <= 0x3C; idx++) {
        cprintf("Reset Register[%s] Value[%02x]\n", RegisterIDString[idx], ResetValue[idx][2]);
        if (REG_RCT != ResetValue[idx][1]) {
            cprintf("USE WRITE!\n");
            writePCFregister(idx, ResetValue[idx][2]);
        }
        regData = readPCFregister(idx);
        cprintf("New   Register[%s] Value[%02x]\n", RegisterIDString[idx], regData);
        cprintf("****************************************%02x\n", idx);
    }
}



